/**
 * 
 */
package dse;

import java.util.*;
import dse.messages.*;

/**
 * @author Sam
 *
 */

public class DataShare {
	private int dataObjectID;
	private int index;
	private Collection<Version> versions;
	public static int order = 0;
	public static int failure;
	public static Version initVersion = new Version(0,0);
	private Collection<Version> stableVersions;
	private Hashtable<Version, Integer> candidateStableVersions;
	private HashMap<Transaction, TreeSet<Version>> curReads;
	private Version bv;
	private TreeMap<Version, LinkedList<Node>> volatileVersions;
	
	public static long[] size = new long[10000];
	public static long[] count = new long[10000];
	public static int[] maxSize = new int[10000];
	static{
		for(int i=0; i<10000; i++){
			size[i] = 30;
			count[i] = 30;
			maxSize[i] = 1;
		}
	}
	
	public DataShare(DataObject dobj, int shareIndex){
		dataObjectID = dobj.id();
		index = shareIndex;
		if(order == 1)
		{
			versions = new TreeSet<Version>();
			stableVersions = new TreeSet<Version>();
		}
		else
		{
			versions = new LinkedList<Version>();
			stableVersions = new LinkedList<Version>();
		}
		versions.add(initVersion);
		stableVersions.add(initVersion);
		candidateStableVersions = new Hashtable<Version, Integer>();
		curReads = null;
		bv = null;
	}
	
	public int getDataObjectID(){
		return dataObjectID;
	}
	public int getIndex(){
		return index;
	}
	public int getVersionSize(){
		return versions.size();
	}
	
	//public void setFirstRead(Version v){
	//	this.firstRead = v;
	//}
	
	public void addStableVersion(Version v){
		if(this.candidateStableVersions.get(v) == null)
			this.candidateStableVersions.put(v, 1);
		else
			this.candidateStableVersions.put(v, this.candidateStableVersions.get(v)+1);
		
		if(this.candidateStableVersions.get(v) == 2*failure+1){
			this.candidateStableVersions.remove(v);
			if(order == 1){
				this.stableVersions.add(v);
			}
			else{
				LinkedList<Version> sv = (LinkedList<Version>)this.stableVersions;
				sv.addFirst(v);
			}
			//if(stableVersions.size() > 40)
				//this.removeOldVersion();
		}
	}
	
	public TreeSet<Version> getVersions(){
		TreeSet<Version> vs =  (TreeSet<Version>)versions;
		return vs;
	}
	public LinkedList<Version> getVersionsV2(){
		return (LinkedList<Version>) versions;
	}
	
	public boolean addNewVersion(Version ver){
		if(!this.versions.contains(ver)){
			if(order == 1)
				this.versions.add(ver);
			else{
				LinkedList<Version> vers = (LinkedList<Version>)versions;
				vers.addFirst(ver);
			}
			size[this.dataObjectID-1]+=versions.size();
			count[this.dataObjectID-1] += 1;
			if(maxSize[this.dataObjectID-1] < versions.size()){
				maxSize[this.dataObjectID-1] = versions.size();
			}
			return true;
		}
		return false;
			//System.out.println("After update Share Index: " + index + " " + ver.toString());
	}
	public void removeOldVersion(){
		if(order == 1){
			TreeSet<Version> vers = (TreeSet<Version>)versions;
			TreeSet<Version> svers = (TreeSet<Version>)stableVersions;
			int s = vers.size();
			Version ver = null;
			if(bv == null){
				ver = svers.last();
				while(svers.size()>1){
					svers.pollFirst();
				}
			}
			else{
				while(svers.first().compareTo(bv)<0 && svers.size()>1)
					ver = svers.pollFirst();
			}
			while(ver!=null && vers.first().compareTo(ver) < 0)
				vers.pollFirst();
			if(s>vers.size()){
				size[this.dataObjectID-1] += vers.size();
				count[this.dataObjectID-1] += 1;
			}
		}
		else{
			LinkedList<Version> vers = (LinkedList<Version>)versions;
			LinkedList<Version> svers = (LinkedList<Version>)stableVersions;
			int s = vers.size();
			Version ver = null;
			if(bv == null){
				ver = svers.getFirst();
				while(svers.size()>1){
					svers.pollLast();
				}
			}
			else{
				while(svers.getLast().compareTo(bv)<0 && svers.size()>1)
					ver = svers.pollLast();
			}
			while(ver!=null && vers.size()>5 && vers.getLast().compareTo(ver) != 0)
				vers.pollLast();
			if(s>vers.size()){
				size[this.dataObjectID-1] += vers.size();
				count[this.dataObjectID-1] += 1;
			}
		}
	}
	
	public Version getPrevious(Version ver){
		if(order == 1){
			TreeSet<Version> vers = (TreeSet<Version>)versions;
			return vers.lower(ver);
		}
		else{
			LinkedList<Version> vers = (LinkedList<Version>)versions;
			Iterator<Version> itr = vers.iterator();
			while(itr.hasNext()){
				Version v = itr.next();
				if(v.compareTo(ver) == 0){
					return itr.next();
				}
			}
			return null;
		}
	}
	
	public Version getLatest(){
		if(order == 1){
			TreeSet<Version> vers = (TreeSet<Version>)versions;
		    //System.out.println("Read Share Index: " + index + " " + vers.last().toString());
			return vers.last();
		}
		else{
			LinkedList<Version> vers = (LinkedList<Version>)versions;
			return vers.getFirst();
		}
	}
	
	public void updateVersion(Version v){
		this.versions.clear();
		this.versions.add(v);
	}
	
	public Version getDesignatedVersion(Version v){
		if(versions.contains(v)){
			return v;
		}
		else
			return null;
	}
	
	public void addCurReads(Transaction trx, Version v){
		if(this.curReads == null)
			this.curReads = new HashMap<Transaction, TreeSet<Version>>();
		if(this.curReads.get(trx) == null)
			this.curReads.put(trx, new TreeSet<Version>());
		this.curReads.get(trx).add(v);
	}
	
	public void removeCurReads(Transaction i, boolean multi){
		if(this.curReads == null)
			return;
		this.curReads.remove(i);
		/*if(multi){
			if(this.curReads.size() == 0){
				bv = null;
				this.removeOldVersion();
			}
			else{
				bv = this.getCurReads();
				this.removeOldVersion();
			}
		}*/
	}
	
	public boolean hasSent(Transaction i, Version v){
		if((curReads == null) || (curReads.get(i) == null))
			return false;
		else
			return curReads.get(i).contains(v);
	}
	
	public Version getShareWithVersion(Version v){
		if(versions.contains(v))
			return v;
		else if((volatileVersions!=null) && (volatileVersions.containsKey(v)))
			return v;
		else
			return null;
	}
	
	public Version getCurReads(){
		if((curReads == null) || (curReads.size() == 0))
			return null;
		Collection<TreeSet<Version>> versions = this.curReads.values();
		Iterator<TreeSet<Version>> itr = versions.iterator();
		Version v = itr.next().first();
		
		while (itr.hasNext()){
			Version v2 = itr.next().first();
			if(v2.compareTo(v) < 0)
				v = v2;
		}
		return v;
	}
	
	public Version getCurReads(Transaction trx){
		if((curReads == null) || (curReads.get(trx) == null))
			return null;
		return curReads.get(trx).first();
	}
	
	public HashMap<Transaction, TreeSet<Version>> getIncompleteRead(){
		return this.curReads;
	}
	
	//two level svr
	/*public void AddStableVersionTLO(Version v){
		this.stableVersions.add(v);
	}*/
	public void SVRTLO(Version v){
		TreeSet<Version> vers = (TreeSet<Version>)versions;
		int s = vers.size();
		
		while(vers.first().compareTo(v) < 0)
			vers.pollFirst();
		if(s>vers.size()){
			size[this.dataObjectID-1] += vers.size();
			count[this.dataObjectID-1] += 1;
		}
	}
	
	public void addVolatileVersion(Version v){
		if(this.volatileVersions == null)
			this.volatileVersions = new TreeMap<Version, LinkedList<Node>>();
		if(this.volatileVersions.get(v) == null)
			this.volatileVersions.put(v, new LinkedList<Node>());
	}
	
	public void addVolatileVersion(Version v, Node n){
		if(this.volatileVersions == null)
			this.volatileVersions = new TreeMap<Version, LinkedList<Node>>();
		if(this.volatileVersions.get(v) == null)
			this.volatileVersions.put(v, new LinkedList<Node>());
		this.volatileVersions.get(v).add(n);
	}
	
	public void removeVolatileVersion(Version v){
		if(this.volatileVersions != null)
			this.volatileVersions.remove(v);
	}
	
	public TreeMap<Version, LinkedList<Node>> getVolatileVersions(){
		return this.volatileVersions;
	}
	
	public int addCandidateStableVersion(Version v){
		if(this.candidateStableVersions.get(v) == null)
			this.candidateStableVersions.put(v, 1);
		else
			this.candidateStableVersions.put(v, this.candidateStableVersions.get(v)+1);
		int count = this.candidateStableVersions.get(v);
		if(count == 2*failure+1)
			this.candidateStableVersions.remove(v);
		return count;
	}
	
	public void SVR(Version bvn){
				
		if(order == 1){
			TreeSet<Version> vers = (TreeSet<Version>)versions;
			int s = vers.size();
			
			while((vers.size()>0) && (vers.first().compareTo(bvn) < 0))
				vers.pollFirst();
			if(s>vers.size()){
				size[this.dataObjectID-1] += vers.size();
				count[this.dataObjectID-1] += 1;
				if(maxSize[this.dataObjectID-1] < versions.size()){
					maxSize[this.dataObjectID-1] = versions.size();
				}
			}
		}
		else{
			LinkedList<Version> vers = (LinkedList<Version>)versions;
			int s = vers.size();
			while(vers.size() > 0 && vers.getLast().compareTo(bvn) != 0)
				vers.pollLast();
			
			if(s>vers.size()){
				size[this.dataObjectID-1] += vers.size();
				count[this.dataObjectID-1] += 1;
				if(maxSize[this.dataObjectID-1] < versions.size()){
					maxSize[this.dataObjectID-1] = versions.size();
				}
			}
		}
	}
}